/**
 * SPDX-FileCopyrightText: 2023-2025 Sangfor Technologies Inc.
 * SPDX-License-Identifier: Mulan PSL v2
 */
/**
 * create by clf on 2018-11-1
 * 进行深拷贝的函数
 * @param  param1, param2, param3, ... 需要进行拷贝及合并的对象 可参考jQuery.extend的使用方法
 * @return 新的拷贝对象
 *
 * extend(obj) 单一参数，直接对obj对象进行拷贝
 * extend(origin, obj1, obj2) 根据origin, obj1, obj2三个对象生成一个新的对象，相同字段，后面的会覆盖前面的
 */

import { isObject } from './typeof';

const toString = Object.prototype.toString;

type ArrayItem<T> = T extends Array<infer K> ? K : never

type ExtendTwo<T, U> = T extends Array<SafeAny> ? 
    Array<ExtendTwo<ArrayItem<T>, ArrayItem<U>>>: 
    {
        [key in Exclude<keyof T, keyof U>]: T[key]
    } & {
        [key in Exclude<keyof U, keyof T>]: U[key]
    } & {
        [key in Extract<keyof T, keyof U>]: ExtendTwo<T[key], U[key]>
    }

function extend<T>(a: T): T
function extend<T, U>(a: T, b: U): ExtendTwo<T, U>
function extend(...origin: unknown[]): SafeAny
function extend (...orgin: unknown[]): SafeAny {
    let args = orgin.slice(1);
    let origin = orgin[0];
    if (toString.call(origin) !== '[object Object]' && toString.call(origin) !== '[object Array]') {
        return origin;
    }

    let ret = isObject(origin) ? Object.create(null) : [];

    args.forEach(item => {

        // 判断传入的参数的类型是否都和要返回的对象的类型相同，不同则抛出错误进行提醒
        if (toString.call(item) !== toString.call(ret)) {
            throw new Error('Please provide the same type of parameters !');
        }

        if (isObject(item)) {
            for (let prop in item) {
                if (item.hasOwnProperty(prop)) {
                    if (ret.hasOwnProperty(prop)  &&
                        toString.call(ret[prop]) === toString.call(item[prop]) &&
                        (toString.call(ret[prop]) === '[object Object]' ||
                        toString.call(ret[prop]) === '[object Array]')) {
                        ret[prop] = extend(ret[prop], item[prop]);
                    } else {
                        ret[prop] = extend(item[prop]);
                    }
                }
            }
        } else if (Array.isArray(item)) {
            for (let i = 0, len = item.length; i < len; i++) {
                if (ret.hasOwnProperty(i) &&
                    toString.call(ret[i]) === toString.call(item[i]) &&
                    (toString.call(item[i]) === '[object Object]' ||
                    toString.call(item[i]) === '[object Array]')) {
                    ret[i] = extend(ret[i], item[i]);
                } else {
                    ret[i] = extend(item[i]);
                }
            }
        }
    });

    return ret;
}

export {
    extend
};

